﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;

namespace BuzzX8.Xmpp.StreamElements
{
    public abstract class CompositeElement<T> : StreamElement
    {
        private ICollection<T> items;
        private XmlNamespace contentNamespace;

        protected CompositeElement(string name) 
            : base(name)
        {
        }

        protected CompositeElement(string name, string namespaceUri)
            : base(name, namespaceUri)
        {
        }

        protected CompositeElement(string name, string namespaceName, string namespaceUri) 
            : base(name, namespaceName, namespaceUri)
        {
        }

        protected CompositeElement(string name, params T[] children)
            : base(name)
        {
            foreach (T element in children) Elements.Add(element);
        }

        public XmlNamespace ContentNamespace
        {
            get
            {
                if (contentNamespace == null) contentNamespace = new XmlNamespace("", "");
                return contentNamespace;
            }
            set
            {
                if (value != null) ValidateContentNamespace(value);
                contentNamespace = value;
            }
        }

        private void ValidateContentNamespace(XmlNamespace ns)
        {
            if (!string.IsNullOrEmpty(ns.Name)) throw new ArgumentException("Content name space must be without name");
        }

        public ICollection<T> Elements
        {
            get { return items ?? (items = CreateElementsCollection()); }
        }

        protected virtual ICollection<T> CreateElementsCollection()
        {
            return new List<T>();
        }

        public void AddElement(T element)
        {
            Elements.Add(element);
        }

        public void AddElements(params T[] elements)
        {
            foreach (T element in elements) AddElement(element);
        }

        public StreamElement FindElement(string elementName)
        {
            if (elementName == null) throw new ArgumentNullException("elementName");
            if (elementName == "") throw new ArgumentOutOfRangeException("elementName");

            return Elements.OfType<StreamElement>().FirstOrDefault(e => e.Name == elementName);
        }

        public TElement FindElement<TElement>(string elementName)  where TElement : StreamElement
        {
            return (TElement)FindElement(elementName);
        }

        protected override void WriteStartElement(XmlWriter writer)
        {
            if (ContentNamespace.IsEmpty)
            {
                base.WriteStartElement(writer);
                return;
            }
            if (Namespace.IsEmpty) writer.WriteStartElement(Name, ContentNamespace.Uri);
            if (!Namespace.IsEmpty && !ContentNamespace.IsEmpty)
            {
                base.WriteStartElement(writer);
                writer.WriteAttributeString("xmlns", ContentNamespace.Uri);
            }
        }

        protected override void WritePayload(XmlWriter writer)
        {
            base.WritePayload(writer);
            WriteElements(writer);
        }

        protected abstract void WriteElements(XmlWriter writer);
    }
}
